package com.dmz.workflow.service.impl;

import com.alibaba.fastjson.JSON;
import com.dmz.workflow.cmd.GetProcessDefinitionInfoCmd;
import com.dmz.workflow.constant.ExceptionMessageConstants;
import com.dmz.workflow.constant.LanguageExpressionConstants;
import com.dmz.workflow.constant.MsgContentEnum;
import com.dmz.workflow.constant.ProcessDefinitionConstants;
import com.dmz.workflow.dao.FlowableFormMapper;
import com.dmz.workflow.dto.ApprovalNode;
import com.dmz.workflow.dto.BranchNode;
import com.dmz.workflow.dto.ExamineGroup;
import com.dmz.workflow.param.PeopleMaintainMemberParam;
import com.dmz.workflow.req.BranchPredictionReq;
import com.dmz.workflow.req.ChitchatReq;
import com.dmz.workflow.req.ProcessDefinitionReq;
import com.dmz.workflow.service.*;
import com.dmz.workflow.util.*;
import com.dmz.workflow.vo.IntermediateNodeVo;
import com.dmz.workflow.vo.StaffInfoVo;
import com.dmz.workflow.vo.TaskFormVo;
import com.dmz.workflow.wrapper.MemberFeignWrapper;
import com.dmz.workflow.wrapper.RoleFeignWrapper;
import com.echronos.commons.Result;
import com.echronos.commons.enums.ErrorMsgEnum;
import com.echronos.commons.model.RequestUser;
import com.echronos.commons.utils.RequestUserUtils;
import com.echronos.imc.api.resp.ImcSessionResp;
import com.echronos.system.feign.IMemberFeign;
import com.echronos.system.feign.IOrganizationFeign;
import com.echronos.system.resp.MemberResp;
import com.echronos.system.resp.member.MemberDetailsResp;
import com.echronos.system.resp.member.MemberPowerResp;
import com.dmz.workflow.entity.FormInfo;
import com.dmz.workflow.entity.ProcessRelations;
import com.echronos.workflow.service.*;
import com.echronos.workflow.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl;
import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext;
import org.flowable.common.engine.impl.javax.el.ExpressionFactory;
import org.flowable.common.engine.impl.javax.el.ValueExpression;
import org.flowable.engine.FormService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProcessDefinitionServiceImpl implements ProcessDefinitionService {
    @Autowired
    protected RepositoryService repositoryService;
    @Autowired
    protected ManagementService managementService;

    @Resource
    private ImcComponent imcComponent;

    @Autowired
    private FlowableFormMapper formMapper;

    @Autowired
    private FlowableFormService flowableFormService;

    @Autowired
    private ProcessInstanceService processInstanceService;

    @Autowired
    private ApprovalNodeService approvalNodeService;

    @Autowired
    private IOrganizationFeign organizationFeign;

    @Autowired
    private RoleFeignWrapper roleFeignWrapper;

    @Autowired
    private IMemberFeign memberFeign;

    @Autowired
    private MemberFeignWrapper memberFeignWrapper;

    @Autowired
    private WorkflowDiscussionService workflowDiscussionService;

    @Autowired
    private WorkflowDictionaryService workflowDictionaryService;

    @Autowired
    private MessageContentWrapper messageContentWrapper;

    @Autowired
    private FormService formService;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private WorkflowProcessRelationsService workflowProcessRelationsService;

    @Autowired
    private ProcessMatrixService processMatrixService;

    @Override
    public ProcessDefinition getProcessDefinitionById(String processDefinitionId) {
        GetProcessDefinitionInfoCmd definitionInfoCmd = new GetProcessDefinitionInfoCmd(processDefinitionId, null, null);
        return managementService.executeCommand(definitionInfoCmd);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activate(ProcessDefinitionReq actionRequest) {
        FormInfo flowableForm = new FormInfo();
        String processDefinitionId = actionRequest.getProcessDefinitionId();
        ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
        if (!processDefinition.isSuspended()) {
            throw new FlowableException("Process definition is not suspended with id " + processDefinitionId);
        }
        repositoryService.activateProcessDefinitionById(processDefinitionId, actionRequest.isIncludeProcessInstances(), new Date());
        flowableForm.setStatus(0);
        flowableForm.setProcessDefinitionId(processDefinitionId);
        formMapper.updateInfoById(flowableForm);
    }

    /**
     * 停用流程 （挂起流程定义）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspend(ProcessDefinitionReq actionRequest) {
        FormInfo flowableForm = new FormInfo();
        String processDefinitionId = actionRequest.getProcessDefinitionId();

        ProcessDefinition processDefinition = getProcessDefinitionById(processDefinitionId);
        if (processDefinition.isSuspended()) {
            throw new FlowableException("Process definition is already suspended with id " + processDefinitionId);
        }
        repositoryService.suspendProcessDefinitionById(processDefinition.getId(), actionRequest.isIncludeProcessInstances(), new Date());
        flowableForm.setStatus(1);
        flowableForm.setProcessDefinitionId(processDefinitionId);
        formMapper.updateInfoById(flowableForm);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<?> createChitchat(ChitchatReq chitchatReq) {
        RequestUser user = RequestUserUtils.getUser();
        ProcessRelations processRelations = workflowProcessRelationsService.queryProcessSessionId(chitchatReq.getProcessInstanceId());
        if (ObjectUtils.isEmpty(processRelations)) {
            ImcSessionResp imcSessionResp = imcComponent.imcCreateGroup(user.getCompanyId(), chitchatReq.getUserIds(), chitchatReq.getName(), user.getId());
            if (ObjectUtils.isNotEmpty(imcSessionResp) && ObjectUtils.isNotEmpty(imcSessionResp.getId())) {
                imcComponent.imcContentSender(messageContentWrapper.generateNotCommentMessageBody(MsgContentEnum.MESSAGE_REPEAL_CONTENT, chitchatReq.getProcessInstanceId()),
                        user.getId(), chitchatReq.getUserIds(), imcSessionResp.getId());
                workflowProcessRelationsService.save(ProcessRelations.builder().processInstanceId(chitchatReq.getProcessInstanceId())
                        .userIds(StringUtils.join(chitchatReq.getUserIds(), LanguageExpressionConstants.LANGUAGE_COMMA))
                        .sessionId(imcSessionResp.getId()).build());
            } else {
                return Result.build(ErrorMsgEnum.NORMAL_ERR.getErrorCode(), ErrorMsgEnum.NORMAL_ERR.getMsg());
            }
            return Result.build(imcSessionResp);
        } else {
            ImcSessionResp imcSessionResp = new ImcSessionResp();
            imcSessionResp.setId(processRelations.getSessionId());
            return Result.build(imcSessionResp);
        }
    }

    @Override
    public Result<List<IntermediateNodeVo>> userInputByValue(BranchPredictionReq branchPredictionReq) {
        Integer memberId = RequestUserUtils.getUser().getMemberId();
        String processJson = approvalNodeService.queryProcessJson(branchPredictionReq.getProcessDefinitionId());
        if (ObjectUtils.isEmpty(processJson)) {
            return Result.build(ErrorMsgEnum.NORMAL_ERR.getErrorCode(), ExceptionMessageConstants.PROCESS_DEFINITION_NOT_EXIST);
        }
        List<ApprovalNode> approvalNodes = JSON.parseArray(processJson, ApprovalNode.class);
        List<IntermediateNodeVo> newProcessNodes = new ArrayList<>();
        Map<String, ApprovalNode> nodeMap = new HashMap<>();
        if (ObjectUtils.isNotEmpty(approvalNodes)) {
            for (ApprovalNode approvalNode : approvalNodes) {
                nodeMap.put(approvalNode.getThisId(), approvalNode);
            }
            ApprovalNode currentNode = approvalNodes.get(1);
            int i = 0;
            do {
                List<BranchNode> conditions = currentNode.getConditions();
                String title = "";
                for (BranchNode condition : conditions) {
                    title = condition.getTitle();
                }
                IntermediateNodeVo intermediateNodeVo = new IntermediateNodeVo();
                intermediateNodeVo.setNodeTitle(title);
                intermediateNodeVo.setNodeId(currentNode.getThisId());
                intermediateNodeVo.setCreateTime(LocalDateTime.now());
                intermediateNodeVo.setSort(i++);
                intermediateNodeVo.setNodeType(currentNode.getType());
                intermediateNodeVo.setIsOptional(currentNode.getIsOptional());
                intermediateNodeVo.setScope(currentNode.getScope());
                intermediateNodeVo.setIsEmpty(currentNode.getIsEmpty());
                intermediateNodeVo.setIsMultiple(currentNode.getIsMultiple());
                intermediateNodeVo.setStatus(currentNode.getStatus());
                intermediateNodeVo.setIsCheckbox(currentNode.getIsCheckbox());
                intermediateNodeVo.setExamineGroups(currentNode.getExamineGroups());
                intermediateNodeVo.setApproveAlert(currentNode.getApproveAlert());
                intermediateNodeVo.setApproveAlertContent(currentNode.getApproveAlertContent());
                intermediateNodeVo.setLevel(currentNode.getLevel());
                intermediateNodeVo.setCascade(currentNode.getCascade());
                // 选择多人的时候注入会签状态
                intermediateNodeVo.setActivity(ObjectUtils.isNotEmpty(currentNode.getActivity()) ? currentNode.getActivity() : ProcessDefinitionConstants.CONSECUTIVE_SIGN);
                intermediateNodeVo.setFormPermission(ObjectUtils.isNotEmpty(currentNode.getFormPermission()) ? currentNode.getFormPermission() : "{}");
                // 节点人员处理
                intermediateNodeVo.setStaffInfoList(nodeMemberProcess(intermediateNodeVo, currentNode, memberId));
                // 流程条件处理
                String nextId = nodeConditionProcess(branchPredictionReq, currentNode);
                if (!ProcessDefinitionConstants.TERM_NODE.equals(currentNode.getType())) {
                    newProcessNodes.add(intermediateNodeVo);
                }
                if (StringUtils.isEmpty(nextId)) {
                    return Result.build(new ArrayList<>());
                }
                // 得到下一个节点对应的键
                currentNode = nodeMap.get(nextId);
            } while (!currentNode.getType().contains(ProcessDefinitionConstants.EVENT_NODE));
        }
        return Result.build(newProcessNodes);
    }

    @Override
    public Result<TaskFormVo> queryTaskFormAndUsedConditions(String processDefinitionId) {
        //获取当前用户并校验权限
        Integer memberId = RequestUserUtils.getUser().getMemberId();
        permissionService.validateReadPermissionOnProcessDefinition(String.valueOf(memberId), processDefinitionId);
        Object renderedStartForm = formService.getRenderedStartForm(processDefinitionId);
        Set<String> usedConditions = approvalNodeService.queryUsedConditions(processDefinitionId);
        String processJson = approvalNodeService.queryProcessJson(processDefinitionId);
        if (ObjectUtils.isEmpty(processJson)) {
            return Result.build(ErrorMsgEnum.NORMAL_ERR.getErrorCode(), ExceptionMessageConstants.PROCESS_DEFINITION_NOT_EXIST);
        }
        List<ApprovalNode> approvalNodes = JSON.parseArray(processJson, ApprovalNode.class);
        String starterFormPermission = approvalNodes.get(0).getFormPermission();
        FormInfo formInfo = flowableFormService.queryFormInfoByProcessDefinitionId(processDefinitionId);
        TaskFormVo taskFormVo = TaskFormVo.builder().signature(formInfo.getSignature()).approvalAdviceType(formInfo.getApprovalAdviceType())
                .approvalAdvice(formInfo.getApprovalAdvice())
                .usedConditions(usedConditions).renderedStartForm(renderedStartForm).starterFormPermission(starterFormPermission).build();
        return Result.build(taskFormVo);
    }

    /**
     * 流程节点条件处理
     *
     * @return 返回下一个节点的键
     */
    private String nodeConditionProcess(BranchPredictionReq branchPredictionReq, ApprovalNode currentNode) {
        List<BranchNode> branchNodeList = currentNode.getConditions();
        if (ObjectUtils.isEmpty(branchNodeList)) {
            throw new FlowableException("节点条件参数错误");
        }
        Map<String, Object> formData = ConditionalDataProcessing.getStringObjectMap(branchPredictionReq.getParameter());
        String nextId = "";
        for (BranchNode node : branchNodeList) {
            if (ObjectUtils.isNotEmpty(node.getCriteria())) {
                String elExpression = ElExpressionUtil.generateElExpression(node.getCriteria());
                Boolean execute = checkFormDataByRuleEl(elExpression, formData);
                log.info("预判参数为:{},预判表达式为:{},预判结果为:{}", formData, elExpression, execute);
                if (execute) {
                    nextId = node.getNextId();
                    break;
                }
            } else {
                nextId = node.getNextId();
            }
        }
        return nextId;
    }

    /**
     * 流程中的人员信息处理
     */
    private List<StaffInfoVo> nodeMemberProcess(IntermediateNodeVo intermediateNodeVo, ApprovalNode currentNode, Integer memberId) {
        List<StaffInfoVo> staffList = new ArrayList<>();
        // 发起节点人员处理
        if (ProcessDefinitionConstants.START_NODE_NAME.equals(currentNode.getThisId())) {
            List<MemberDetailsResp> memberDetailsResps = memberFeignWrapper.queryMemberDetailList(Collections.singletonList(memberId));
            intermediateNodeVo.setStatusType(ProcessDefinitionConstants.STARTER_SELECTION_DISABLE);
            getMemberDetail(intermediateNodeVo, staffList, memberDetailsResps);
        }
        // 抄送节点人员处理
        if (ProcessDefinitionConstants.DUPLICATE_NODE.equals(currentNode.getType())) {
            if (ProcessDefinitionConstants.STARTER_CC_SELECTION_ENABLE.equals(currentNode.getIsOptional())) {
                intermediateNodeVo.setStatusType(ProcessDefinitionConstants.STARTER_SELECTION_ENABLE);
            } else {
                intermediateNodeVo.setStatusType(ProcessDefinitionConstants.STARTER_SELECTION_DISABLE);
            }
            getExamineGroupsInfo(currentNode, intermediateNodeVo, staffList);
        }
        List<StaffInfoVo> staffInfoVos = null;
        // 审批节点人员处理
        if (!ProcessDefinitionConstants.START_NODE_NAME.equals(currentNode.getThisId()) && ProcessDefinitionConstants.CHECK_NODE.equals(currentNode.getType())) {
            staffInfoVos = processApproveNodeMember(intermediateNodeVo, currentNode, memberId);
        } else {
            if (ObjectUtils.isNotEmpty(staffList)) {
                staffInfoVos = BeanUtilsExt.copyObjectListProperties(staffList, StaffInfoVo.class);
            }
        }
        return staffInfoVos;
    }

    private List<StaffInfoVo> processApproveNodeMember(IntermediateNodeVo intermediateNodeVo, ApprovalNode currentNode, Integer memberId) {
        List<StaffInfoVo> staffInfoVos = new ArrayList<>();
        if (ProcessDefinitionConstants.STARTER_SELECTION_MEMBER.equals(currentNode.getStatus())) {
            intermediateNodeVo.setStatusType(ProcessDefinitionConstants.STARTER_SELECTION_ENABLE);
        } else {
            intermediateNodeVo.setStatusType(ProcessDefinitionConstants.STARTER_SELECTION_DISABLE);
        }
        // 1为指定成员(候选人)，2为发起人自选 4.部门负责人 5.角色 6.矩阵角色
        if (ProcessDefinitionConstants.ASSIGNED_MEMBER.equals(currentNode.getStatus())) {
            getExamineGroupsInfo(currentNode, intermediateNodeVo, staffInfoVos);
        }
        if (ProcessDefinitionConstants.DEPARTMENT_LEADER_MEMBER.equals(currentNode.getStatus())) {
            Result<List<MemberResp>> leaderResult = memberFeign.queryLeader(memberId);
            if (ObjectUtils.isNotEmpty(leaderResult.getData())) {
                List<MemberResp> leaderMembers = leaderResult.getData();
                if (ObjectUtils.isNotEmpty(leaderMembers)) {
                    getMemberInfo(intermediateNodeVo, staffInfoVos, leaderMembers);
                }
            }
        }
        if (ProcessDefinitionConstants.ROLE_MEMBER.equals(currentNode.getStatus())) {
            List<MemberResp> memberResp = roleFeignWrapper.queryRoleAllMember(currentNode.getRoleId(), RequestUserUtils.getUser().getCompanyId());
            getMemberInfo(intermediateNodeVo, staffInfoVos, memberResp);
        }

        // 矩阵角色管理
        if (ProcessDefinitionConstants.MATRIX_ROLE.equals(currentNode.getStatus())) {
            List<PeopleMaintainMemberParam> peopleMaintainMemberParams = processMatrixService.getMatrixRolePeopleMaintain(currentNode.getMatrixId(), currentNode.getRoleName()).getData();
            if (!CollectionUtils.isEmpty(peopleMaintainMemberParams)) {
                List<Integer> memberIds = peopleMaintainMemberParams.stream().map(PeopleMaintainMemberParam::getMemberId).collect(Collectors.toList());
                List<MemberResp> memberResps = new ArrayList<>();
                // 本地循环拼接，减少服务调用
                memberIds.forEach(integer -> {
                    MemberResp memberResp = new MemberResp();
                    memberResp.setId(integer);
                    memberResps.add(memberResp);
                });
                getMemberInfo(intermediateNodeVo, staffInfoVos, memberResps);
            }
        }

        if (ObjectUtils.isEmpty(staffInfoVos)) {
            // 处理节点是否设置为自动转交功能
            if (!ProcessDefinitionConstants.ASSIGNED_MEMBER.equals(currentNode.getStatus()) && ProcessDefinitionConstants.NODE_AUTO_TRANSFER_SETTING.equals(currentNode.getIsEmpty())) {
                MemberPowerResp memberPowerResp = memberFeign.queryAdmin(RequestUserUtils.getUser().getCompanyId());
                if (ObjectUtils.isNotEmpty(memberPowerResp)) {
                    StaffInfoVo staffInfoVo = new StaffInfoVo();
                    staffInfoVo.setIdNode(currentNode.getThisId());
                    staffInfoVo.setTypeNode(currentNode.getType());
                    staffInfoVo.setMemberId(memberPowerResp.getId());
                    staffInfoVo.setUserId(memberPowerResp.getUserId());
                    staffInfoVo.setMemberName(memberPowerResp.getName());
                    staffInfoVo.setUrl(memberPowerResp.getAvatar());
                    staffInfoVos.add(staffInfoVo);
                }
            }
        }
        return staffInfoVos;
    }

    /**
     * 根据不同类型获取成员信息
     */
    private void getExamineGroupsInfo(ApprovalNode currentNode, IntermediateNodeVo intermediateNodeVo, List<StaffInfoVo> staffInfoVoList) {
        if (currentNode.getExamineGroups() != null) {
            for (ExamineGroup examineGroup : currentNode.getExamineGroups()) {
                switch (examineGroup.getUserType()) {
                    case ProcessDefinitionConstants.ASSIGNED_DEPARTMENT_MEMBER:
                        // 调取系统的根据部门id查询人员 查出来返回的是list 需遍历筛入属性
                        Result<List<MemberResp>> members = organizationFeign.getBatchMembers(Collections.singletonList(examineGroup.getUserGroup()));
                        getMemberInfo(intermediateNodeVo, staffInfoVoList, members.getData());
                        break;
                    case ProcessDefinitionConstants.ASSIGNED_ROLE_MEMBER:
                        // 调取系统的根据角色id查询人员 查出来返回的是list 需遍历筛入属性
                        List<MemberResp> memberResp = roleFeignWrapper.queryRoleAllMember(examineGroup.getUserGroup(), RequestUserUtils.getUser().getCompanyId());
                        getMemberInfo(intermediateNodeVo, staffInfoVoList, memberResp);
                        break;
                    default:
                        // 拿成员id调取系统服务 查询信息，防止用户更改用户信息
                        List<Integer> memberIds = Collections.singletonList(examineGroup.getUserGroup());
                        Result<List<MemberResp>> memberList = memberFeign.getMemberList(memberIds);
                        getMemberInfo(intermediateNodeVo, staffInfoVoList, memberList.getData());
                        break;
                }
            }
        }
    }

    private static Boolean checkFormDataByRuleEl(String el, Map<String, Object> formData) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        for (Object object : formData.keySet()) {
            String formKey = object.toString();
            if (formData.get(formKey) != null) {
                context.setVariable(formKey, factory.createValueExpression(formData.get(formKey), formData.get(formKey).getClass()));
            }
        }
        try {
            ValueExpression e = factory.createValueExpression(context, el, Boolean.class);
            return (Boolean) e.getValue(context);
        } catch (Exception exception) {
            log.info("Expression parse error{}", exception.getMessage());
            return false;
        }

    }

    public void getMemberInfo(IntermediateNodeVo intermediateNodeVo, List<StaffInfoVo> list, List<MemberResp> members) {
        List<Integer> memberIds = new ArrayList<>();
        if (ObjectUtils.isNotEmpty(members)) {
            for (MemberResp member : members) {
                if (ObjectUtils.isNotEmpty(member)) {
                    memberIds.add(member.getId());
                } else {
                    return;
                }
            }
        } else {
            log.info(intermediateNodeVo.getNodeId() + "节点人员列表为空...");
            return;
        }
        List<MemberDetailsResp> memberDetailsResps = memberFeignWrapper.queryMemberDetailList(memberIds);
        for (MemberDetailsResp memberDetailsResp : memberDetailsResps) {
            StaffInfoVo staffInfoVo = new StaffInfoVo();
            staffInfoVo.setIsRemoved(0);
            staffInfoVo.setMemberId(memberDetailsResp.getMemberId());
            staffInfoVo.setMemberName(memberDetailsResp.getMemberName());
            staffInfoVo.setIdNode(intermediateNodeVo.getNodeId());
            staffInfoVo.setTypeNode(intermediateNodeVo.getNodeType());
            staffInfoVo.setCreateTime(LocalDateTime.now());
            staffInfoVo.setUrl(memberDetailsResp.getAvatar());
            staffInfoVo.setUserId(memberDetailsResp.getUserId());
            list.add(staffInfoVo);
        }
    }

    public void getMemberDetail(IntermediateNodeVo intermediateNodeVo, List<StaffInfoVo> list, List<MemberDetailsResp> members) {
        for (MemberDetailsResp member : members) {
            if (ObjectUtils.isEmpty(member) || ObjectUtils.isEmpty(member.getMemberId())) {
                log.info(intermediateNodeVo.getNodeId() + "节点人员列表为空...");
                return;
            }
            StaffInfoVo staffInfoVo = new StaffInfoVo();
            staffInfoVo.setMemberId(member.getMemberId());
            staffInfoVo.setMemberName(member.getMemberName());
            staffInfoVo.setIdNode(intermediateNodeVo.getNodeId());
            staffInfoVo.setTypeNode(intermediateNodeVo.getNodeType());
            staffInfoVo.setCreateTime(LocalDateTime.now());
            staffInfoVo.setUrl(member.getAvatar());
            staffInfoVo.setUserId(member.getUserId());
            list.add(staffInfoVo);
        }
    }
}